package models

import (
	"3rd/mongo"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
		goerrors "github.com/go-errors/errors"
	"time"
)

type StationProfile struct {
	StationID string `bson:"_id" json:"stationID"`
	StationNO string `bson:"stationNO" json:"stationNO"`
	Qid       string `bson:"qid" json:"qid"`
}

type Station struct {
	StationProfile    `,inline`
	Master            string     `bson:"master" json:"master" valid:"Required"`
	Providers         []Provider `bson:"providers" json:"providers"`
	Postman           []*Postman `bson:"postman" json:"postman"`
	Telephone         []string   `bson:"telephone" json:"telephone"`
	MobilePhone       []string   `bson:"mobilePhone" json:"mobilePhone"  valid:"Required"`
	DistributionRange string     `bson:"distributionRange" json:"distributionRange"`
	Address           string     `bson:"address"json:"address"  valid:"Required"`
	State             int        `bson:"state" json:"state"`
	Location          GeoJson    `bson:"location"  json:"location"`
	Created           int64      `bson:"created" json:"created"`
}

func includeMyself(qid string, men []*Postman) bool {
	b := false
	for _, v := range men {
		if qid == v.Qid {
			b = true
			break
		}
	}
	return b
}

func AddStation(station *Station) (string, error) {
	station.Created = time.Now().Unix()
	station.StationID = bson.NewObjectId().Hex()
	station.Location.Type = "point"
	if !includeMyself(station.StationProfile.Qid, station.Postman) {
		station.Postman = append(station.Postman, &Postman{Qid: station.StationProfile.Qid, Name: "我自己", MobilePhone: station.MobilePhone[0]})
	}

	// TODO: whether the station has been added?

	return station.StationID, mongo.DO(db_station, col_station, func(c *mgo.Collection) (err error) {
		if err := c.Insert(station); err != nil {
			return goerrors.New(err)
		}
		return nil
	})
}

func GetStationByID(id, qid string, station *Station) error {
	return mongo.DO(db_station, col_station, func(c *mgo.Collection) error {
		query := bson.M{"_id": id, "qid": qid}
		if err := c.Find(query).One(station); err != nil {
			return goerrors.New(err)
		}
		return nil
	})
}

func GetAuthedStations() ([]Station, error) {
	return nil, nil
}

func GetUnauthedStations() ([]Station, error) {
	return nil, nil
}

func GetToauthStations() ([]Station, error) {
	return nil, nil
}

func AuthStation(stationID string) error {
	return nil
}

func RejectStation(stationID, failedReason string) error {
	return nil
}

func GetMyStation(qid, stationID string, station *Station) error {
	return mongo.DO(db_station, col_station, func(c *mgo.Collection) error {
		if err := c.Find(bson.M{"_id": stationID, "qid": qid}).One(station); 
		err != nil {
			return goerrors.New(err)
		}
		return nil
	})
}

//func StationExists(qid, stationID string) error {
//	return mongo.DO(db_station, col_station, func(c *mgo.Collection) error {
//		return c.Find(bson.M{"_id": stationID, "qid": qid}).seleclt(bson.M{"_id", 1}).One(station)
//	})
//}

func UpdateStation(station *Station) error {
	//station.Created = time.Now().Unix()
	station.Location.Type = "point"
	return mongo.DO(db_station, col_station, func(c *mgo.Collection) error {
		selector := bson.M{"_id": station.StationID, "qid": station.Qid}
		if err := c.Update(selector, station); err != nil {
			return goerrors.New(err)
		}
		return nil
	})
}

func GetStations(qid string, stations *[]Station) error {
	return mongo.DO(db_station, col_station, func(c *mgo.Collection) error {
		if err := c.Find(bson.M{"qid": qid}).All(stations); err != nil {
			return goerrors.New(err)
		}
		return nil
	})
}

// TODO: deprecated
func GetStationProducts(qid string, stationID string, products *[]Product) error {
	// get providerID from station by stationID && qid
	var station Station
	if err := GetMyStation(qid, stationID, &station); err != nil {
		return goerrors.New(err)
	}
	for _, val := range(station.Providers) {
		*products = append(*products, val.Products...)
	}
	return nil
}

func GetPostman(qid string, stationID string, men *[]*Postman) error {
	return mongo.DO(db_station, col_station, func(c *mgo.Collection) error {
		var station Station
		if err := c.Find(bson.M{"qid": qid, "_id": stationID}).Select(bson.M{"_id": 0, "postman": 1}).One(&station); err != nil {
			return goerrors.New(err)
		}
		*men = station.Postman
		return nil
	})
}

func AddPostman(qid string, stationID string, man Postman) error {
	return mongo.DO(db_station, col_station, func(c *mgo.Collection) error {
		GetPostmanByPhone(man.MobilePhone, &man)
		// TODO: redundancy
		if err := c.Update(bson.M{"qid": qid}, bson.M{"$addToSet": bson.M{"postman": man}});
		err != nil {
			return goerrors.New(err)
		}
		return nil
	})
}

func RmPostman(qid string, stationID string, man Postman) error {
	return mongo.DO(db_station, col_station, func(c *mgo.Collection) error {
		if err := c.Update(bson.M{"qid": qid}, 
		bson.M{"$pull": bson.M{"postman": bson.M{"mobilePhone": man.MobilePhone}}});
		err != nil {
			return goerrors.New(err)
		}
		return nil
	})
}

func UpdatePostman(qid string, stationID string, man Postman) error {
	return mongo.DO(db_station, col_station, func(c *mgo.Collection) error {
		if err := c.Update(
			bson.M{"qid": qid, "postman.mobilePhone": man.MobilePhone},
			bson.M{"$set": bson.M{"postman.$": man}}); err != nil {
				return goerrors.New(err)
			}
			return nil
	})
}

func TotalStation(qid string, from, to int64) (count int, err error) {
	return count, mongo.DO(db_order, col_order, func(c *mgo.Collection) (err error) {
		pipe := c.Pipe([]bson.M{
			{"$match": bson.M{"state": COMPLETE, "station.qid": qid, "created": bson.M{"$gte": from, "$lt": to}}},
			{"$group": bson.M{"_id": nil, "count": bson.M{"$sum": "$sentBuckets"}}},
		})

		var out bson.M
		err = pipe.One(&out)
		if err != nil {
			return goerrors.New(err)
		}
		count = out["count"].(int)
		return
	})
}

func StatsPostman(qid string, from, to int64, men []string, result *[]bson.M) error {
	return mongo.DO(db_order, col_order, func(c *mgo.Collection) (err error) {
		match := bson.M{"state": COMPLETE, "station.qid": qid, "created": bson.M{"$gte": from, "$lt": to}}
		if men != nil && len(men) > 0 {
			match["postman.name"] = bson.M{"$in": men}
		}
		group := bson.M{"_id": "$postman.name", "count": bson.M{"$sum": "$sentBuckets"}}
		pipe := c.Pipe([]bson.M{{"$match": match}, {"$group": group}})

		if err := pipe.All(result); err != nil {
			return goerrors.New(err)
		}
		return nil
	})
}
